<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <title>ESP32串口调试助手</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 20px;
        }

        #container {
            display: flex;
            flex-direction: column;
            height: 95vh;
        }

        #receivedData {
            flex-grow: 1;
            border: 1px solid #ccc;
            padding: 10px;
            overflow-y: auto;
            margin-bottom: 10px;
            white-space: pre-wrap;
            background: #f8f8f8;
            font-family: monospace;
            font-size: 20px;
        }

        #inputArea {
            display: flex;
            margin-bottom: 10px;
        }

        #message {
            flex-grow: 1;
            padding: 8px;
            margin-right: 5px;
        }

        button {
            padding: 8px 15px;
            margin-left: 5px;
        }

        .controls {
            margin: 10px 0;
            display: flex;
            flex-wrap: wrap;
            gap: 15px;
        }

        .status {
            color: #666;
            margin-bottom: 10px;
        }

        .timestamp {
            color: #888;
            font-size: 0.9em;
            margin-right: 8px;
        }

        /* 修改发送和接收颜色 */
        .sent {
            color: #2e7d32; /* 深绿色 - 更鲜明的发送消息颜色 */
            font-weight: bold;
        }

        .received {
            color: #1565c0; /* 深蓝色 - 更鲜明的接收消息颜色 */
            font-weight: bold;
        }

        .system {
            color: #9e9e9e;
            font-style: italic;
        }

        .message-row {
            margin-bottom: 5px;
        }

        .flex-row {
            display: flex;
            align-items: center;
        }
    </style>
</head>

<body>
    <div id="container">
        <h2>ESP32网络串口调试助手</h2>

        <div class="status">
            <div>连接状态: <span id="connStatus">正在连接...</span></div>
            <div>IP地址: <span id="ipAddr">-</span></div>
        </div>

        <div class="controls">
            <div class="flex-row">
                <label><input type="checkbox" id="hexDisplay"> 十六进制显示</label>
                <label><input type="checkbox" id="hexSend"> 十六进制发送</label>
            </div>
            <div class="flex-row">
                <label><input type="checkbox" id="autoScroll" checked> 自动滚动</label>
                <label><input type="checkbox" id="showTimestamp" checked> 显示时间戳</label>
            </div>
            <div class="flex-row">
                <label><input type="checkbox" id="showSent" checked> 显示发送信息</label>
                <label><input type="checkbox" id="showReceived" checked> 显示接收信息</label>
            </div>
        </div>

        <div id="receivedData"></div>

        <div id="inputArea">
            <input type="text" id="message" placeholder="输入要发送的数据 (十六进制用空格分隔)">
            <button onclick="sendMessage()">发送</button>
            <button onclick="clearOutput()">清空</button>
        </div>
    </div>

    <script>
        let websocket;
        const receivedData = document.getElementById('receivedData');
        const hexDisplay = document.getElementById('hexDisplay');
        const hexSend = document.getElementById('hexSend');
        const autoScroll = document.getElementById('autoScroll');
        const showTimestamp = document.getElementById('showTimestamp');
        const showSent = document.getElementById('showSent');
        const showReceived = document.getElementById('showReceived');
        const connStatus = document.getElementById('connStatus');
        const ipAddr = document.getElementById('ipAddr');

        // 格式化时间戳
        function formatTimestamp() {
            if (!showTimestamp.checked) return '';

            const now = new Date();
            const hours = String(now.getHours()).padStart(2, '0');
            const minutes = String(now.getMinutes()).padStart(2, '0');
            const seconds = String(now.getSeconds()).padStart(2, '0');
            const milliseconds = String(now.getMilliseconds()).padStart(3, '0');

            return `<span class="timestamp">[${hours}:${minutes}:${seconds}.${milliseconds}]</span>`;
        }

        // 添加消息到显示区域
        function addMessage(content, type = 'system') {
            console.log('Adding message with type:', type); // 添加调试信息
            if (type === 'sent' && !showSent.checked) return;
            if (type === 'received' && !showReceived.checked) return;

            const timestamp = formatTimestamp();
            const row = document.createElement('div');
            row.className = `message-row ${type}`;
            row.innerHTML = `${timestamp}<span class="${type}">${content}</span>`;
            receivedData.appendChild(row);
            console.log('Message element:', row); // 添加调试信息

            if (autoScroll.checked) {
                receivedData.scrollTop = receivedData.scrollHeight;
            }
        }

        function connectWebSocket() {
            websocket = new WebSocket('ws://' + window.location.hostname + ':81/');
            websocket.binaryType = 'arraybuffer'; // 指定接收二进制数据

            websocket.onopen = () => {
                console.log("WebSocket连接已建立");
                connStatus.textContent = "已连接";
                connStatus.style.color = "green";
                addMessage(">>> WebSocket连接成功", "system");
            };

            websocket.onmessage = (event) => {
                if (event.data instanceof ArrayBuffer) {
                    const bytes = new Uint8Array(event.data);

                    if (hexDisplay.checked) {
                        // 十六进制显示
                        let hexString = '';
                        for (let i = 0; i < bytes.length; i++) {
                            hexString += bytes[i].toString(16).padStart(2, '0') + ' ';
                        }
                        console.log('Received hex data:', hexString); // 添加调试信息
                        addMessage(hexString, "received");
                    } else {
                        // 使用TextDecoder处理UTF-8字符串
                        try {
                            const textDecoder = new TextDecoder('utf-8', { fatal: false });
                            let textString = textDecoder.decode(bytes);
                            // 替换控制字符以便显示
                            textString = textString.replace(/\n/g, '\\n').replace(/\r/g, '\\r');
                            console.log('Received text data:', textString); // 添加调试信息
                            addMessage(textString, "received");
                        } catch (e) {
                            // 如果转换失败，使用原来的方式显示
                            let textString = '';
                            for (let i = 0; i < bytes.length; i++) {
                                if (bytes[i] >= 32 && bytes[i] <= 126) {
                                    textString += String.fromCharCode(bytes[i]);
                                } else if (bytes[i] === 10) {
                                    textString += '\\n';
                                } else if (bytes[i] === 13) {
                                    textString += '\\r';
                                } else {
                                    textString += '.';
                                }
                            }
                            console.log('Received fallback text data:', textString); // 添加调试信息
                            addMessage(textString, "received");
                        }
                    }
                } else {
                    // 文本消息（保留用于系统消息）
                    console.log('Received system message:', event.data); // 添加调试信息
                    addMessage(event.data, "system");
                }
            };

            websocket.onerror = (error) => {
                console.error("WebSocket错误:", error);
                connStatus.textContent = "连接错误";
                connStatus.style.color = "red";
                addMessage(">>> WebSocket错误: " + error.type, "system");
            };

            websocket.onclose = () => {
                console.log("WebSocket连接关闭");
                connStatus.textContent = "已断开";
                connStatus.style.color = "gray";
                addMessage(">>> WebSocket连接关闭", "system");
                // 5秒后尝试重新连接
                setTimeout(connectWebSocket, 5000);
            };
        }

        // 页面加载完成后连接
        window.onload = function () {
            ipAddr.textContent = window.location.hostname;
            connectWebSocket();
        };

        function sendMessage() {
            const messageInput = document.getElementById('message');
            let message = messageInput.value;

            if (message.trim() !== '') {
                if (hexSend.checked) {
                    // 十六进制发送处理
                    const hexBytes = message.trim().split(/\s+/);
                    let uint8Array = new Uint8Array(hexBytes.length);

                    for (let i = 0; i < hexBytes.length; i++) {
                        if (hexBytes[i]) {
                            const byte = parseInt(hexBytes[i], 16);
                            if (!isNaN(byte) && byte >= 0 && byte <= 255) {
                                uint8Array[i] = byte;
                            } else {
                                addMessage(">>> 错误: 无效的十六进制值 '" + hexBytes[i] + "'", "system");
                                messageInput.value = '';
                                return;
                            }
                        }
                    }

                    // 发送二进制数据
                    websocket.send(uint8Array.buffer);
                    addMessage("发送: " + message, "sent");
                } else {
                    // 文本发送
                    websocket.send(message);
                    addMessage("发送: " + message, "sent");
                }
                messageInput.value = '';
            }
        }

        function clearOutput() {
            receivedData.innerHTML = "";
        }

        // 支持Enter键发送
        document.getElementById('message').addEventListener('keyup', (event) => {
            if (event.key === 'Enter') sendMessage();
        });

        // 为选项添加标签点击功能
        document.querySelectorAll('label').forEach(label => {
            label.addEventListener('click', function (e) {
                e.stopPropagation();
            });
        });
    </script>
</body>

</html>